const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

/*函数式的promise实现
function Promise(){
  let p = function(){};
  p.then=()=>{};
  return p;
}
*/

/*
x是then方法接受的参数方法中return的值
,在这里它可能是个promise 也可能是 一个普通值
//TODO 3.1.1、3.1.2、3.2.2、
*/
function resolvePromise(promise2, x, resolve, reject) { //resolve和reject是promise2的resolve和reject
  // x 来取决 promise2 是成功还是失败
  // 为了保证错误代码出现
  if (x === promise2) { //TODO 3.5
    return reject(
      new TypeError(
        "TypeError: Chaining cycle detected for promise #<Promise>11"
      )
    );
  }

  // 怎么判断 x 是不是一个promise(并且有可能是别人的promise，我们要兼容)
  // 判断x 是不是一个promise 如果x是常量 那就直接用这个结果将promise 成功掉即可
  let called;
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    // x到这里才有可能是promise，我们用try，防止我们猜测错误导致报错
    try {
      let then = x.then; // 取then 可能发生异常 (Object.defineProperty(obj,'then',{get(){throw new Error()}}))
      if (typeof then === "function") { // 这里就只能认为他是一个promise 如果是一个空方法 走下面的逻辑报错的话就没法了
        //↓不采用x.then 是防止取多次then 但每次不一样的情况(通过Object.defineProperty与计数器模式...emmm，这有点强词夺理)

        //当return 的x(promise)有结果时 会执行下面通过then方法注册的回调
        then.call(
          x,
          y => {
            // 用刚才取出来的then方法继续使用，不要再次取then方法了
            if (called) return; // 调用成功后 就不能再调用失败(主要是防止别人的promise允许状态互转(executor里resolve了又reject什么的，并且resolve或reject方法内都没有对promise的状态是否已经发生改变过进行验证)，这样就可能把注册的成功回调和失败回调都执行)
            called = true;
            // resolve(y)
            resolvePromise(promise2, y, resolve, reject); //有可能return的那个promise resolve的时候还是一个promise(y) TODO 3.6
            // 递归解析当前x的promise的返回结果，因为promie成功后可能返回的还是一个promise
          },
          r => {
            if (called) return; //和上面成功回调的called共用一个called变量(作用域链查找)
            called = true;
            reject(r);
          }
        );
      } else {
        // x->{then:null}
        resolve(x); // 普通值
      }
    } catch (e) {
      if (called) return; // 只用在可能是别人的promise的这个分支里 利用called防止重复调用即可，其它分支里是我们自己的promise相关的 不存在这个问题
      called = true;
      reject(e);
    }
  } else { //TODO 3.1.1
    // 普通的字符串 number bool
    resolve(x);
  }
}
class Promise {
  constructor(executor) {
    console.log('自己的promise');
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.resolveCallbacks = [];
    this.rejectCallbacks = [];
    let resolve = value => {
      if(value instanceof Promise){ // resolve的还是一个promise TODO 3.9 TODO 4.1
        return value.then(resolve, reject);
      }
      if (this.status === PENDING) {
        this.value = value;
        this.status = FULFILLED;
        this.resolveCallbacks.forEach(fn => fn());
      }
    };
    let reject = reason => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED;
        this.rejectCallbacks.forEach(fn => fn());
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }

  // x 是当前then 成功或者失败函数的返回结果
  // x是不是一个普通值如果普通值 把值直接传递到下一个then中
  // x是一个promise ？ 我需要采用这个x的状态
  // 如果 执行函数出错，直接调用promise2的失败
  then(onFufilled, onRejected) {

    //TODO 3.8 ↓
    onFufilled = typeof onFufilled === "function" ? onFufilled : val => val;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : err => {
          throw err;
        };

    // promise2 要等待当前这次new的promise 执行完后 才能获取到(so用了一个延迟setTimeout)
    let promise2 = new Promise((resolve, reject) => {
      // 此函数会立即执行
      if (this.status === FULFILLED) {
        setTimeout(() => { //TODO 3.7
          try {
            let x = onFufilled(this.value);
            // 看x的返回结果，看一下x 是不是promise，在去让promise2 变成成功或者失败
            resolvePromise(promise2, x, resolve, reject);
            // resolve(x);
          } catch (e) {
            reject(e); //TODO 3.2.1
          }
        });
      }
      if (this.status === REJECTED) {
        setTimeout(() => { //TODO 3.7
          //TODO 3.3.1 ↓
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e); //TODO 3.2.1
          }
        });
      }
      if (this.status === PENDING) {
        this.resolveCallbacks.push(() => {
          // todo...
          setTimeout(() => {
            try {
              let x = onFufilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e); //TODO 3.2.1
            }
          });
        });
        this.rejectCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e); //TODO 3.2.1
            }
          });
        });
      }
    });
    return promise2; // 实现链式调用 TODO 3.4
  }

  //↓非规范强制规定的方法
  catch(errCallback){ //就是then的语法糖
    return this.then(null, errCallback);
  }

  finally(callback){
    let P = this.constructor;
    return this.then(
      value  => P.resolve(callback()).then(() => value),
      reason => P.resolve(callback()).then(() => { throw reason })
    );
  }

  static all(promises){
    return new Promise((resolve,reject)=>{
      let arr = [];
      let index = 0;
      let processData = function(i,y){
        arr[i] = y;
        if(++index/*每次拿到结果后+1*/=== promises.length){
          resolve(arr);
        }
      }
      for(let i =0 ; i< promises.length;i++){
        let value =promises[i];
        if(isPromise(value)){
          value.then(function(y/*y代表all接受的promises中其中一个promise的结果*/){
            // i 对应的结果 就是 y
            processData(i,y);
          },reject)
        }else{
          processData(i,value);
        }
      }
    });
  }

  static race(values) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < values.length; ++i) {
        let current = values[i];
        if (isPromise(current)) {
          current.then(resolve, reject);
        } else {
          resolve(current);
        }
      }
    });
  }

  //Promise.resolve 的如果不是一个promise则是同步的,否则则是异步的(微任务 x.then)
  static resolve(x){
    return new Promise((resolve,reject)=>{
      resolve(x);
    })
  }

  static reject(x){
    return new Promise((resolve,reject)=>{
      reject(x);
    })
  }

}

//测试是否符合promise A+规范
//promise-aplus-tests
// 测试前 必须要加这一段代码
Promise.defer = Promise.deferred = function() {
  let dfd = {}; // ←会围绕这个对象来测试
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};


function isPromise(value){
  if((typeof value === 'object' && value!==null)  || typeof value =='function'){
    if(typeof value.then === 'function'){
      return true;
    }
  }
  return false
}

Promise.promisify = function(fn){
  return function(...args){
    return new Promise(function(resolve,reject){
      fn(...args,function(err,data){
        if(err)return reject(err);
        resolve(data);
      });
    });
  };
};

Promise.promisifyAll = function(obj){
  Object.keys(obj).forEach(key=>{
    if(typeof obj[key]==='function'){
      obj[key+'Async'] = Promise.promisify(obj[key]);
    }
  });
};

module.exports = Promise;
